﻿using System;
using System.Collections.Generic;
using System.Threading;
using log4net;

namespace Interactive.Utility.Cache
{
    /// <summary>
    /// 隔一段时间刷新的缓存
    /// </summary>
    /// <typeparam name="V"></typeparam>
    public class RegularCacheProxy<V> : ICacheProxy<V>
    {
        /// <summary>
        /// 日志记录器
        /// </summary>
        private static readonly ILog myLog = LogManager.GetLogger(typeof (RegularCacheProxy<V>));

        /// <summary>
        /// 获取缓存的方法
        /// </summary>
        private readonly Func<V> getFun;

        /// <summary>
        /// 获取时间间隔,默认不跟新
        /// </summary>
        private readonly TimeSpan getSpan = TimeSpan.Zero;

        /// <summary>
        /// 锁
        /// </summary>
        private readonly object myLock = new object();


        /// <summary>
        /// 是否刷新
        /// </summary>
        private readonly bool refresh;

        private readonly Int16 waitMillseconds = 50;

        /// <summary>
        /// 缓存的值
        /// </summary>
        private V cacheValue;

        /// <summary>
        /// 用来记录上次获取的时间
        /// </summary>
        private DateTime lastGetTime = DateTime.MinValue;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="getFun">跟新缓存的方法</param>
        public RegularCacheProxy(TimeSpan getSpan, Func<V> getFun) : this(getSpan, default(V), getFun, 50)
        {
        }


        public RegularCacheProxy(TimeSpan getSpan, Func<V> getFun, Int16 waitMillseconds)
            : this(getSpan, default(V), getFun, waitMillseconds)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="defaultCachValue">默认值</param>
        /// <param name="getFun">跟新缓存的方法</param>
        /// <param name="waitMillseconds">等锁的毫秒数</param>
        public RegularCacheProxy(TimeSpan getSpan, V defaultCachValue, Func<V> getFun, Int16 waitMillseconds)
        {
            if (getSpan.TotalMilliseconds < 0)
                throw new ArgumentException("获取时间间隔不能为负数!", "getSpan");
            if (getFun == null)
                throw new ArgumentException("获取值的方法不能为空!", "getFun");

            if (getSpan.TotalMilliseconds > 0)
                refresh = true;

            this.getSpan = getSpan;
            cacheValue = defaultCachValue;
            this.waitMillseconds = waitMillseconds;
            this.getFun = getFun;
        }

        #region ICacheProxy<V> Members

        /// <summary>
        /// 获取值
        /// </summary>
        public V Value
        {
            get
            {
                if (!refresh)
                    return cacheValue;

                DateTime now = DateTime.Now;
                if (now > lastGetTime)
                {
                    if (Monitor.TryEnter(myLock, waitMillseconds))
                    {
                        try
                        {
                            if (now > lastGetTime)
                            {
                                lastGetTime = now.Add(getSpan);
                                V temp = getFun();
                                cacheValue = temp;
                            }
                        }
                        catch (Exception exception)
                        {
                            myLog.Error("在获取缓存值的时候出现错误!", exception);
                        }
                        finally
                        {
                            Monitor.Exit(myLock);
                        }
                    }
                }
                return cacheValue;
            }
        }

        /// <summary>
        /// 强制更新缓存,无限等待
        /// </summary>
        /// <returns></returns>
        public bool EnforceRefresh()
        {
            return EnforceRefresh(Timeout.Infinite);
        }

        /// <summary>
        /// 强制更新缓存
        /// </summary>
        /// <param name="millseconds">等待的毫秒数</param>
        /// <returns></returns>
        public bool EnforceRefresh(int millseconds)
        {
            if (Monitor.TryEnter(myLock, millseconds))
            {
                try
                {
                    lastGetTime = DateTime.MinValue;
                    return true;
                }
                finally
                {
                    Monitor.Exit(myLock);
                }
            }
            return false;
        }

        #endregion
    }

    public class RegularCacheProxy<K, V> : ICacheProxy<K, V>
    {
        /// <summary>
        /// 日志记录器
        /// </summary>
        private static readonly ILog myLog = LogManager.GetLogger("CacheProxy<K, V>");

        /// <summary>
        /// 缓存的值
        /// </summary>
        private readonly V defaultValue;

        /// <summary>
        /// 内部容器
        /// </summary>
        private readonly IDictionary<K, CacheWrapper> dictionary;

        /// <summary>
        /// 获取缓存的方法
        /// </summary>
        private readonly Func<K, V> getFun;

        /// <summary>
        /// 获取时间间隔,默认不跟新
        /// </summary>
        private readonly TimeSpan getSpan = TimeSpan.Zero;

        /// <summary>
        /// 锁
        /// </summary>
        private readonly object myLock = new object();


        /// <summary>
        /// 是否刷新
        /// </summary>
        private readonly bool refresh;

        private readonly Int16 waitMillseconds = 50;

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="getFun">跟新缓存的方法</param>
        public RegularCacheProxy(TimeSpan getSpan, Func<K, V> getFun) : this(getSpan, default(V), getFun, 50, 0)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="getFun">跟新缓存的方法</param>
        /// <param name="capacity">初始容量</param>
        public RegularCacheProxy(TimeSpan getSpan, Func<K, V> getFun, int capacity)
            : this(getSpan, default(V), getFun, 50, capacity)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="getFun">跟新缓存的方法</param>
        /// <param name="waitMillseconds">等锁的毫秒数</param>
        public RegularCacheProxy(TimeSpan getSpan, Func<K, V> getFun, Int16 waitMillseconds)
            : this(getSpan, default(V), getFun, waitMillseconds, 0)
        {
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="getFun">跟新缓存的方法</param>
        /// <param name="waitMillseconds">等锁的毫秒数</param>
        /// <param name="capacity">初始容量</param>
        public RegularCacheProxy(TimeSpan getSpan, Func<K, V> getFun, Int16 waitMillseconds, int capacity)
            : this(getSpan, default(V), getFun, waitMillseconds, capacity)
        {
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="defaultCachValue">默认值</param>
        /// <param name="getFun">跟新缓存的方法</param>
        /// <param name="waitMillseconds">等锁的毫秒数</param>
        /// <param name="capacity">初始容量</param>
        public RegularCacheProxy(TimeSpan getSpan, V defaultCachValue, Func<K, V> getFun, Int16 waitMillseconds,
                                 int capacity)
            : this(getSpan, defaultCachValue, getFun, waitMillseconds, capacity, false)
        {
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSpan">更新缓存的时间间隔</param>
        /// <param name="defaultCachValue">默认值</param>
        /// <param name="getFun">跟新缓存的方法</param>
        /// <param name="waitMillseconds">等锁的毫秒数</param>
        /// <param name="capacity">初始容量</param>
        /// <param name="isRecycle">是否回收,如果是超过指定的容量就会回收,否则无限增长</param>
        public RegularCacheProxy(TimeSpan getSpan, V defaultCachValue, Func<K, V> getFun, Int16 waitMillseconds,
                                 int capacity,
                                 bool isRecycle)
        {
            if (getSpan.TotalMilliseconds < 0)
                throw new ArgumentException("获取时间间隔不能为负数!", "getSpan");
            if (getFun == null)
                throw new ArgumentException("获取值的方法不能为空!", "getFun");


            if (getSpan.TotalMilliseconds > 0)
                refresh = true;
            this.getSpan = getSpan;
            if (!isRecycle)
            {
                dictionary = capacity > 0
                                 ? new Dictionary<K, CacheWrapper>(capacity)
                                 : new Dictionary<K, CacheWrapper>();
            }
            else
            {
                if (capacity < 1)
                    throw new ArgumentException("当回收时,容量不能小于1", "capacity");
                dictionary = new RecycleDictionary<K, CacheWrapper>(capacity);
            }
            defaultValue = defaultCachValue;
            this.waitMillseconds = waitMillseconds;
            this.getFun = getFun;
        }

        #endregion

        #region ICacheProxy<K,V> Members

        /// <summary>
        /// 获取值
        /// </summary>
        public V GetValue(K key)
        {
            CacheWrapper cacheWrapper;
            if (!dictionary.TryGetValue(key, out cacheWrapper))
            {
                lock (myLock)
                {
                    if (!dictionary.TryGetValue(key, out cacheWrapper))
                    {
                        cacheWrapper = new CacheWrapper(this);
                        dictionary.Add(key, cacheWrapper);
                    }
                }
            }
            return cacheWrapper.GetValue(key);
        }

        /// <summary>
        /// 强制更新缓存,无限期等待
        /// </summary>
        /// <param name="key">键</param>
        /// <returns>结果</returns>
        public bool EnforceRefresh(K key)
        {
            return EnforceRefresh(key, Timeout.Infinite);
        }


        /// <summary>
        /// 强制更新缓存
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="millseconds">等待的毫秒数</param>
        /// <returns></returns>
        public bool EnforceRefresh(K key, int millseconds)
        {
            CacheWrapper cacheWrapper;
            if (dictionary.TryGetValue(key, out cacheWrapper)) return cacheWrapper.EnforceRefresh(millseconds);
            return true;
        }

        #endregion

        #region Nested type: CacheWrapper

        /// <summary>
        /// 缓存的包装器
        /// </summary>
        private class CacheWrapper
        {
            /// <summary>
            /// 锁
            /// </summary>
            private readonly object myLock = new object();

            private readonly RegularCacheProxy<K, V> regularCacheProxy;

            /// <summary>
            /// 缓存的值
            /// </summary>
            private V cacheValue;

            /// <summary>
            /// 用来记录上次获取的时间
            /// </summary>
            private DateTime lastGetTime = DateTime.MinValue;

            public CacheWrapper(RegularCacheProxy<K, V> regularCacheProxy)
            {
                this.regularCacheProxy = regularCacheProxy;
                cacheValue = regularCacheProxy.defaultValue;
            }

            /// <summary>
            /// 强制更新缓存
            /// </summary>
            /// <param name="millseconds">等待的毫秒数</param>
            /// <returns></returns>
            public bool EnforceRefresh(int millseconds)
            {
                if (Monitor.TryEnter(myLock, millseconds))
                {
                    try
                    {
                        lastGetTime = DateTime.MinValue;
                        return true;
                    }
                    finally
                    {
                        Monitor.Exit(myLock);
                    }
                }
                return false;
            }


            /// <summary>
            /// 获取值
            /// </summary>
            public V GetValue(K key)
            {
                if (!regularCacheProxy.refresh)
                    return cacheValue;

                DateTime now = DateTime.Now;
                if (now > lastGetTime)
                {
                    if (Monitor.TryEnter(myLock, regularCacheProxy.waitMillseconds))
                    {
                        try
                        {
                            if (now > lastGetTime)
                            {
                                lastGetTime = now.Add(regularCacheProxy.getSpan);
                                V temp = regularCacheProxy.getFun(key);
                                cacheValue = temp;
                            }
                        }
                        catch (Exception exception)
                        {
                            myLog.Error("在获取缓存值的时候出现错误!", exception);
                        }
                        finally
                        {
                            Monitor.Exit(myLock);
                        }
                    }
                }
                return cacheValue;
            }
        }

        #endregion
    }


    /// <summary>
    /// 列表和容量,经常在获取能翻页的数据列表中用到
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ListAndCount<T>
    {
        /// <summary>
        /// 列表
        /// </summary>
        public IList<T> List { get; set; }

        /// <summary>
        /// 容量
        /// </summary>
        public int Count { get; set; }
    }

    /// <summary>
    /// 通用分页时用到的key
    /// </summary>
    public struct CommonPageKey4
    {
        /// <summary>
        /// 通用Id
        /// </summary>
        public readonly int ownerId;

        /// <summary>
        /// 当前页数,注意是从1开始
        /// </summary>
        public readonly int pageNow;

        /// <summary>
        /// 页面大小
        /// </summary>
        public readonly int pageSize;

        /// <summary>
        ///  类型
        /// </summary>
        public readonly byte type;

        public CommonPageKey4(byte type, int ownerId, int pageNow, int pageSize)
        {
            this.type = type;
            this.ownerId = ownerId;
            this.pageNow = pageNow;
            this.pageSize = pageSize;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (obj.GetType() != typeof (CommonPageKey4)) return false;
            return Equals((CommonPageKey4) obj);
        }


        public bool Equals(CommonPageKey4 other)
        {
            return other.type == type && other.ownerId == ownerId && other.pageNow == pageNow &&
                   other.pageSize == pageSize;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = type.GetHashCode();
                result = (result*397) ^ ownerId;
                result = (result*397) ^ pageNow;
                result = (result*397) ^ pageSize;
                return result;
            }
        }

        public override string ToString()
        {
            return type + "_" + ownerId + "_" + pageNow + "_" + pageSize;
        }
    }


    public struct CommonPageKey2
    {
        /// <summary>
        /// 当前页数,注意是从1开始
        /// </summary>
        public readonly int pageNow;

        /// <summary>
        /// 页面大小
        /// </summary>
        public readonly int pageSize;

        public CommonPageKey2(int pageNow, int pageSize)
        {
            this.pageNow = pageNow;
            this.pageSize = pageSize;
        }


        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (obj.GetType() != typeof (CommonPageKey2)) return false;
            return Equals((CommonPageKey2) obj);
        }

        public bool Equals(CommonPageKey2 other)
        {
            return other.pageNow == pageNow && other.pageSize == pageSize;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return (pageNow*397) ^ pageSize;
            }
        }

        public override string ToString()
        {
            return pageNow + "_" + pageSize;
        }
    }


    public struct CommonPageKey3
    {
        /// <summary>
        /// 通用Id
        /// </summary>
        public readonly int ownerId;

        /// <summary>
        /// 当前页数,注意是从1开始
        /// </summary>
        public readonly int pageNow;

        /// <summary>
        /// 页面大小
        /// </summary>
        public readonly int pageSize;


        public CommonPageKey3(int pageNow, int pageSize, int ownerId)
        {
            this.ownerId = ownerId;
            this.pageNow = pageNow;
            this.pageSize = pageSize;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (obj.GetType() != typeof (CommonPageKey3)) return false;
            return Equals((CommonPageKey3) obj);
        }


        public bool Equals(CommonPageKey3 other)
        {
            return other.pageNow == pageNow && other.pageSize == pageSize && other.ownerId == ownerId;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = pageNow;
                result = (result*397) ^ pageSize;
                result = (result*397) ^ ownerId;
                return result;
            }
        }

        public override string ToString()
        {
            return pageNow + "_" + pageSize + "_" + ownerId;
        }
    }
}